import threading

if __name__ == '__main__':
    from util.buffer import Buffer
else:
    from .buffer import Buffer

from threading import RLock


class Pipe:
    """
    管道是一种能够自动运行的，带有输入和输出功能的对象。put()函数可以往管道里输入对象，对象经过转换和传递，能被自动发送到输出端。调用
    get()函数可以获取输出的对象。管道的意义在于，其输入和输出是异步的，没有要求输入和输出必须同步。这就为用异步方式或者流水线方式处理数据带来了
    便利。
    """
    def __init__(self, name=None):
        self.lock = RLock()
        self.status = 'ready'
        # self._obj_listensers = []
        # self._logger = logging.getLogger() if logger is None else logger
        self.name = None
        self.stopping = False
        self.set_name(name)

    def get_status(self):
        """
        获取管道的状态。管道的状态有3种：'ready', 'running', 'pause'
        """
        with self.lock:
            return self.status

    def get_lock(self):
        return self.lock

    def get_stopping(self):
        with self.lock:
            return self.stopping

    def start(self):
        """
        用线程安全的方式启动这个管道。该函数会调用函数_start()，用户应该重定义后者以实现管道的启动逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            if self.status == 'running':
                return
            assert self.status == 'ready', '管道%s只有在就绪(ready)状态下才能启动, 当前状态: %s' % (self.name, self.status)
            self.stopping = False
            try:
                self._start()
            finally:
                self.status = 'running'

    def _start(self):
        pass

    def stop(self):
        """
        用线程安全的方式关闭这个管道。该函数会调用函数_stop()，用户应该重定义后者以实现管道的关闭逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            if self.status == 'ready':
                return
            assert self.status in ('running', 'pause'), '管道%s只有在运行(running)或暂停(pause)状态下才能停止，当前状态: %s' % (
                self.name, self.status)
            self.stopping = True
            try:
                self._stop()
            finally:
                self.status = 'ready'

    def _stop(self):
        pass

    def pause(self):
        """
        用线程安全的方式暂停这个管道。该函数会调用函数_pause()，用户应该重定义后者以实现管道的暂停逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            assert self.status == 'running', '管道%s只有在运行(running)状态下才能暂停, 当前状态: %s' % (self.name, self.status)
            try:
                self._pause()
            finally:
                self.status = 'pause'

    def _pause(self):
        pass

    def resume(self):
        """
        用线程安全的方式恢复这个管道的运行。该函数会调用函数_resume()，用户应该重定义后者以实现管道的恢复运行逻辑，此时用户不需要考虑线程安全以及管道的状态。
        """
        with self.lock:
            assert self.status == 'pause', '管道%s只有在暂停(pause)状态下才能恢复, 当前状态: %s' % (self.name, self.status)
            try:
                self._resume()
            finally:
                self.status = 'running'

    def _resume(self):
        pass

    def get(self):
        """
        从当前管道中返回一个对象，如果没有则等待
        """
        return None

    def put(self, obj):
        """
        把obj插入到当前管道中。
        :param obj 被插入的对象或对象列表
        """
        pass

    def get_name(self):
        return self.name

    def set_name(self, name):
        self.name = get_name(name)


class BufferPipe(Pipe):
    def __init__(self, buffer_size=0, name=None):
        super(BufferPipe, self).__init__(name)
        self.buffer_size = buffer_size
        self.buffer = None

    def put(self, obj):
        self.buffer.put(obj)

    def get(self):
        return self.buffer.get()

    def _start(self):
        self.buffer = Buffer(self.buffer_size)
        self.buffer.start()

    def _stop(self):
        self.buffer.stop()
        self.buffer = None

    def _pause(self):
        self.buffer.pause()

    def _resume(self):
        self.buffer.resume()


class Func(Pipe):
    def __init__(self, func, buffer_size, multiple: bool = False, args=None, kwargs: dict = None, name=None):
        """
        Func带有一个Buffer和一个func函数，后者用来转换buffer的输出。Buffer输出的对象将被传递给func函数，
        func的结果才是最终的输出。如果buffer是None，则func被直接调用。
        :param buffer_size 缓冲器的大小, 0表示大小不受限制，-1表示没有缓冲器
        :param func 转换函数，buffer的输出obj将作为第一个参数，其他参数由*args和**kwargs决定
        :param multiple True表示从buffer中获取多个对象，组成一个列表，然后再传递给func函数, func的输出也被认为
        是一个对象的列表。False表示buffer中缓存的对象是一个一个按次序传递给func函数的，对每一个输入func也仅产生一个
        输出。
        :param args 调用func时除第一个参数之外的其他可变实参
        :param kwargs 调用func时的命名实参
        """
        super(Func, self).__init__(name=name)
        self.func = func
        self.multiple = multiple
        self.args = [] if args is None else args
        self.kwargs = {} if kwargs is None else kwargs
        self.buffer_size = buffer_size
        self.input_buffer = None
        self.output_buffer = None

    def _start(self):
        if self.buffer_size >= 0:
            self.input_buffer = Buffer(self.buffer_size)
            self.input_buffer.start()
        self.output_buffer = []

    def _stop(self):
        if self.input_buffer is not None:
            self.input_buffer.stop()
        self.output_buffer.clear()

    def _pause(self):
        if self.input_buffer is not None:
            self.input_buffer.pause()

    def _resume(self):
        if self.input_buffer is not None:
            self.input_buffer.resume()

    def put(self, obj):
        if self.input_buffer is None:
            self.func(obj, *self.args, **self.kwargs)
        else:
            self.input_buffer.put(obj)

    def get(self):
        with self.lock:
            if len(self.output_buffer) == 0:
                if self.input_buffer is None:
                    obj = self.func(*self.args, **self.kwargs)
                else:
                    if self.multiple:
                        obj = self.input_buffer.get_many()
                    else:
                        obj = self.input_buffer.get()
                    obj = self.func(obj, *self.args, **self.kwargs)
                if self.multiple:
                    self.output_buffer.extend(obj)
                else:
                    self.output_buffer.append(obj)
            obj = self.output_buffer.pop(0)
        return obj


class Identical(Pipe):
    def put(self, value):
        raise Exception('Head的put()方法是不能调用的')

    def get(self):
        obj = self.func(*self.args, **self.kwargs)
        return obj


class Head(Pipe):
    def __init__(self, func, args=None, kwargs: dict = None, name=None):
        """
        Head是一种特殊的管道，只有输出，没有输入，因此只能单独使用或者在Sequence中作为第一个子管道存在。Head的put()方法是不能调用的。
        :param func 转换函数，buffer的输出obj将作为第一个参数，其他参数由*args和**kwargs决定
        :param args 调用func时除第一个参数之外的其他可变实参
        :param kwargs 调用func时的命名实参
        """
        super(Head, self).__init__(name=name)
        self.func = func
        self.args = [] if args is None else args
        self.kwargs = {} if kwargs is None else kwargs

    def put(self, value):
        raise Exception('Head的put()方法是不能调用的')

    def get(self):
        obj = self.func(*self.args, **self.kwargs)
        return obj


class Tail(Pipe):
    def __init__(self, func, args=None, kwargs: dict = None, name=None):
        """
        Tail是一种特殊的管道，只有输入，没有输出，因此只能单独使用或者在Sequence中作为最后一个子管道存在。Tail的get()方法是不能调用的。
        :param func 转换函数，Tail的输入将作为第一个参数传递给func，其他参数由*args和**kwargs决定
        :param args 调用func时除第一个参数之外的其他可变实参
        :param kwargs 调用func时的命名实参
        """
        super(Tail, self).__init__(name=name)
        self.func = func
        self.args = [] if args is None else args
        self.kwargs = {} if kwargs is None else kwargs

    def put(self, obj):
        self.func(obj, *self.args, **self.kwargs)

    def get(self, multiple=False):
        raise Exception('Tail的get()方法是不能调用的')


class Sequence(Pipe):
    def __init__(self, pipes=None, name=None):
        """
        创建一个序列管道，它是由多个管道连接而成的。每相邻的两个管道之间有线程自动把前者的输出输送到后者的输入中。
        :param pipes 子管道列表或者元组
        """
        super(Sequence, self).__init__(name=name)
        self.pipes = [] if pipes is None else [p for p in pipes]

    def add_pipe(self, pipe: Pipe):
        with self.lock:
            assert self.status == 'ready', '管道序列%s只有在就绪(ready)状态下才能添加子管道, 当前状态: %s' % (self.name, self.status)
            self.pipes.append(pipe)

    def _start(self):
        for p in self.pipes:
            p.start()
        for i in range(len(self.pipes)-1):
            p1 = self.pipes[i]
            p2 = self.pipes[i+1]
            th = _SequenceSubThread(self, p1, p2)
            th.start()

    def _stop(self):
        for p in self.pipes:
            p.stop()

    def _pause(self):
        for p in self.pipes:
            p.pause()

    def _resume(self):
        for p in self.pipes:
            p.resume()

    def get(self):
        return self.pipes[-1].get()

    def put(self, obj):
        self.pipes[0].put(obj)


class _SequenceSubThread(threading.Thread):
    def __init__(self, sequence, pipe1, pipe2):
        super(_SequenceSubThread, self).__init__(daemon=True)
        self.sequence = sequence
        self.pipe1 = pipe1
        self.pipe2 = pipe2

    def run(self):
        while not self.sequence.stopping:
            obj = self.pipe1.get()
            self.pipe2.put(obj)


class Parallel(Pipe):
    def __init__(self, pipes=None, same_input=True, name=None):
        """
        :param pipes 子管道的列表或者元组
        :param same_input True表示输入到这个管道的对象被传递给每一个子管道，False表示输入是一个聚合，其中
        的每一个元素被按顺序传递给相应的子管道。
        """
        super(Parallel, self).__init__(name)
        self.pipes = [] if pipes is None else [e for e in pipes]
        self.same_input = same_input

    def add_pipe(self, pipe):
        with self.lock:
            self.pipes.append(pipe)
            return len(self.pipes) - 1

    def get_pipes(self):
        with self.lock:
            return self.pipes.copy()

    def set_pipe(self, index, pipe:Pipe):
        with self.lock:
            assert self.status == 'ready', '并行管道%s只有在就绪(ready)状态下才能添加子管道, 当前状态: %s' % (self.name, self.status)
            self.pipes[index] = pipe

    def get_pipe(self, index):
        with self.lock:
            return self.pipes[index]

    def _start(self):
        for pipe in self.pipes:
            pipe.start()

    def _stop(self):
        for pipe in self.pipes:
            pipe.stop()

    def _pause(self):
        for pipe in self.pipes:
            pipe.pause()

    def _resume(self):
        for pipe in self.pipes:
            pipe.resume()

    def get(self):
        with self.lock:
            return [pipe.get() for pipe in self.pipes]

    def put(self, obj):
        if self.same_input:
            for p in self.pipes:
                p.put(obj)
        else:
            for p, obj in zip(self.pipes, obj):
                p.put(obj)
